package com.kamistoat.meimeimall.meimeimallmember.dubbo;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.kamistoat.meimeimall.common.To.memberTo.LoginSuccessTo;
import com.kamistoat.meimeimall.common.To.memberTo.MemberReceiveAddressTo;
import com.kamistoat.meimeimall.common.To.memberTo.RegisterMemberTo;
import com.kamistoat.meimeimall.common.annotation.DubboAndHTTPExceptionCatch;
import com.kamistoat.meimeimall.common.constant.AuthConstant;
import com.kamistoat.meimeimall.common.dubboInterface.MemberDubboInterface;
import com.kamistoat.meimeimall.common.exception.BizCodeEnum;
import com.kamistoat.meimeimall.common.exception.RRException;
import com.kamistoat.meimeimall.common.threadlocal.ThreadLocalContextHolder;
import com.kamistoat.meimeimall.common.utils.JwtUtils;
import com.kamistoat.meimeimall.common.utils.R;
import com.kamistoat.meimeimall.meimeimallmember.entity.MemberLoginLogEntity;
import com.kamistoat.meimeimall.meimeimallmember.service.MemberLoginLogService;
import com.kamistoat.meimeimall.meimeimallmember.service.MemberReceiveAddressService;
import com.kamistoat.meimeimall.meimeimallmember.service.MemberService;
import io.seata.core.constants.DubboConstants;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * Dubbo接口实现
 */
@Service
@Component
public class MemberDubboInterfaceImpl implements MemberDubboInterface {
    @Autowired
    MemberService memberService;
    @Autowired
    MemberLoginLogService memberLoginLogService;
    @Autowired
    MemberReceiveAddressService memberReceiveAddressService;
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    /**
     * 用户注册RPC
     *
     * @param registerMemberTo RPC间To类
     * @return R对象
     */
    @DubboAndHTTPExceptionCatch
    @Override
    public R register(RegisterMemberTo registerMemberTo) {
        // 检测手机号。若有异常则被AOP捕获，否则正常返回 R.ok()
        memberService.registerMember(registerMemberTo);
        return R.ok();
    }

    /**
     * 用户登录RPC
     *
     * @param loginAccount 账号
     * @param password     密码
     * @param IP           IP
     * @param IPAddr       Ip地址
     * @return R对象. 内含 "jwtString" - jwt字符串
     */
    @DubboAndHTTPExceptionCatch
    @Override
    public R login(String loginAccount, String password, String IP, String IPAddr) {
        try {
            LoginSuccessTo loginSuccessTo = memberService.login(loginAccount, password);
            // 没有异常抛出，则证明登录成功。为这个登录令牌生成一个令牌Token
            String token = IdWorker.getTimeId();
            loginSuccessTo.setToken(token);

            // 记录用户登录记录
            MemberLoginLogEntity loginLog = new MemberLoginLogEntity();
            loginLog.setMemberId(loginSuccessTo.getId());
            loginLog.setCreateTime(new Date());
            loginLog.setLoginType(1);
            loginLog.setIp(IP);
            loginLog.setCity(IPAddr);
            memberLoginLogService.save(loginLog);

            // 将登录令牌添加到缓存中。缓存的key是 login:user:userId:loginToken
            stringRedisTemplate.opsForValue().set(
                    AuthConstant.LOGIN_USER_REDIS_PREFIX + loginSuccessTo.getId() + ":" + loginSuccessTo.getToken(),
                    JSON.toJSONString(loginSuccessTo));

            // 制作 JwtString，放在R对象中返回
            HashMap<String, Object> jwtMap = new HashMap<>();
            jwtMap.put(AuthConstant.JWT_USER_TOKEN, loginSuccessTo.getToken());
            jwtMap.put(AuthConstant.JWT_USER_ID, loginSuccessTo.getId());
            jwtMap.put(AuthConstant.JWT_USER_NAME, loginSuccessTo.getUsername());
            String jwtString = JwtUtils.createJwtString(jwtMap);
            return R.ok().put("jwtString", jwtString);
        } catch (Exception exception) {
            if (exception instanceof RRException) {
                return R.error(((RRException) exception).getCode(), ((RRException) exception).getMsg());
            } else {
                return R.error(BizCodeEnum.UNKNOW_EXCEPTION.getCode(), exception.getMessage());
            }
        }
    }

    /**
     * 用户登出RPC
     *
     * @return R对象.
     */
    @Override
    public R loginout() {
        LoginSuccessTo loginSuccessTo = JSON.parseObject((String) ThreadLocalContextHolder.get(AuthConstant.LOGIN_USER_THREADLOCAL_NAME), LoginSuccessTo.class);
        stringRedisTemplate.delete(AuthConstant.LOGIN_USER_REDIS_PREFIX + loginSuccessTo.getId() + ":" + loginSuccessTo.getToken());
        return R.ok();
    }

    /**
     * 查询用户的所有收货地址RPC
     *
     * @param memberId 用户Id
     * @return R对象 内含 "memberReceiveAddressToList" - List<{@link com.kamistoat.meimeimall.common.To.memberTo.MemberReceiveAddressTo}>
     */
    @DubboAndHTTPExceptionCatch
    @Override
    public R getOrderAddress(Long memberId) {
        List<MemberReceiveAddressTo> memberReceiveAddressToList = memberReceiveAddressService.getMemberReceiveAddressToList(memberId);
        return R.ok().put("memberReceiveAddressToList", JSON.toJSONString(memberReceiveAddressToList));
    }

    /**
     * 根据收货地址Id查询收货地址
     *
     * @param addrId 地址id
     * @return R对象 内含 "memberReceiveAddressTo" - MemberReceiveAddressTo
     */
    @DubboAndHTTPExceptionCatch
    @Override
    public R getAddressById(Long addrId) {
        LoginSuccessTo loginSuccessTo = JSON.parseObject((String) ThreadLocalContextHolder.get(AuthConstant.LOGIN_USER_THREADLOCAL_NAME), LoginSuccessTo.class);
        MemberReceiveAddressTo memberReceiveAddressTo = memberReceiveAddressService.getReceiveAddressById(loginSuccessTo.getId(), addrId);
        return R.ok().put("memberReceiveAddressTo", JSON.toJSONString(memberReceiveAddressTo));
    }

    /**
     * 根据收货地址Id计算运费
     *
     * @param addrId 地址Id
     * @return R对象 内含 "fare" - BigDecimal
     */
    @DubboAndHTTPExceptionCatch
    @Override
    public R getFare(Long addrId) {
        BigDecimal fare = memberReceiveAddressService.getFare(addrId);
        return R.ok().put("fare", fare);
    }
}
